import { join } from "path";
import { readFileSync } from "fs";
import { clients, db, resources, siteResources } from "@server/db";
import { randomInt } from "crypto";
import { exitNodes, sites } from "@server/db";
import { eq, and } from "drizzle-orm";
import { __DIRNAME } from "@server/lib/consts";

// Load the names from the names.json file
const dev = process.env.ENVIRONMENT !== "prod";
let file;
if (!dev) {
    file = join(__DIRNAME, "names.json");
} else {
    file = join("server/db/names.json");
}
export const names = JSON.parse(readFileSync(file, "utf-8"));

export async function getUniqueClientName(orgId: string): Promise<string> {
    let loops = 0;
    while (true) {
        if (loops > 100) {
            throw new Error("Could not generate a unique name");
        }

        const name = generateName();
        const count = await db
            .select({ niceId: clients.niceId, orgId: clients.orgId })
            .from(clients)
            .where(and(eq(clients.niceId, name), eq(clients.orgId, orgId)));
        if (count.length === 0) {
            return name;
        }
        loops++;
    }
}

export async function getUniqueSiteName(orgId: string): Promise<string> {
    let loops = 0;
    while (true) {
        if (loops > 100) {
            throw new Error("Could not generate a unique name");
        }

        const name = generateName();
        const count = await db
            .select({ niceId: sites.niceId, orgId: sites.orgId })
            .from(sites)
            .where(and(eq(sites.niceId, name), eq(sites.orgId, orgId)));
        if (count.length === 0) {
            return name;
        }
        loops++;
    }
}

export async function getUniqueResourceName(orgId: string): Promise<string> {
    let loops = 0;
    while (true) {
        if (loops > 100) {
            throw new Error("Could not generate a unique name");
        }

        const name = generateName();
        const [resourceCount, siteResourceCount] = await Promise.all([
            db
                .select({ niceId: resources.niceId, orgId: resources.orgId })
                .from(resources)
                .where(
                    and(eq(resources.niceId, name), eq(resources.orgId, orgId))
                ),
            db
                .select({
                    niceId: siteResources.niceId,
                    orgId: siteResources.orgId
                })
                .from(siteResources)
                .where(
                    and(
                        eq(siteResources.niceId, name),
                        eq(siteResources.orgId, orgId)
                    )
                )
        ]);
        if (resourceCount.length === 0 && siteResourceCount.length === 0) {
            return name;
        }
        loops++;
    }
}

export async function getUniqueSiteResourceName(
    orgId: string
): Promise<string> {
    let loops = 0;
    while (true) {
        if (loops > 100) {
            throw new Error("Could not generate a unique name");
        }

        const name = generateName();
        const [resourceCount, siteResourceCount] = await Promise.all([
            db
                .select({ niceId: resources.niceId, orgId: resources.orgId })
                .from(resources)
                .where(
                    and(eq(resources.niceId, name), eq(resources.orgId, orgId))
                ),
            db
                .select({
                    niceId: siteResources.niceId,
                    orgId: siteResources.orgId
                })
                .from(siteResources)
                .where(
                    and(
                        eq(siteResources.niceId, name),
                        eq(siteResources.orgId, orgId)
                    )
                )
        ]);
        if (resourceCount.length === 0 && siteResourceCount.length === 0) {
            return name;
        }
        loops++;
    }
}

export async function getUniqueExitNodeEndpointName(): Promise<string> {
    let loops = 0;
    const count = await db.select().from(exitNodes);
    while (true) {
        if (loops > 100) {
            throw new Error("Could not generate a unique name");
        }

        const name = generateName();

        for (const node of count) {
            if (node.endpoint.includes(name)) {
                loops++;
                continue;
            }
        }

        return name;
    }
}

export function generateName(): string {
    const name = (
        names.descriptors[randomInt(names.descriptors.length)] +
        "-" +
        names.animals[randomInt(names.animals.length)]
    )
        .toLowerCase()
        .replace(/\s/g, "-");

    // clean out any non-alphanumeric characters except for dashes
    return name.replace(/[^a-z0-9-]/g, "");
}
